import DEFAULTS from './defaults';
import TEMPLATE from './template';
import render from './render';
import events from './events';
import handlers from './handlers';
import methods from './methods';
import others from './others';
import {
    BUTTONS,
    CLASS_CLOSE,
    CLASS_FADE,
    CLASS_FIXED,
    CLASS_FULLSCREEN,
    CLASS_HIDE,
    CLASS_INVISIBLE,
    DATA_ACTION,
    EVENT_CLICK,
    EVENT_LOAD,
    EVENT_READY,
    NAMESPACE,
    REGEXP_SPACES,
    WINDOW,
} from './constants';
import {
    addClass,
    addListener,
    assign,
    dispatchEvent,
    forEach,
    getResponsiveClass,
    hyphenate,
    isFunction,
    isNumber,
    isPlainObject,
    isString,
    isUndefined,
    removeListener,
    setData,
    setStyle,
    toggleClass,
} from './utilities';

const AnotherViewer = WINDOW.Viewer;

class Viewer {
    /**
     * Create a new Viewer.
     * @param {Array} images - The images for viewing.
     * @param {Object} [options={}] - The configuration options.
     */
    constructor(images, options = {}) {
        this.images = images;
        this.element = document.createElement("div"); // 创建一个div元素，完成事件监听和派发
        this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
        this.action = false;
        this.fading = false;
        this.fulled = false;
        this.hiding = false;
        this.imageClicked = false;
        this.imageData = {};
        this.index = this.options.initialViewIndex;
        this.isShown = false;
        this.length = 0;
        this.played = false;
        this.playing = false;
        this.pointers = {};
        this.ready = false;
        this.showing = false;
        this.timeout = false;
        this.tooltipping = false;
        this.viewed = false;
        this.viewing = false;
        this.wheeling = false;
        this.zooming = false;
        this.init();
    }

    init() {
        const {element, options} = this;

        if (element[NAMESPACE]) {
            return;
        }

        element[NAMESPACE] = this;


        this.length = this.images.length;
        this.initBody();

        // Override `transition` option if it is not supported
        if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
            options.transition = false;
        }
    }

    build() {
        if (this.ready) {
            return;
        }

        const {element, options} = this;
        const template = document.createElement('div');

        template.innerHTML = TEMPLATE;

        const viewer = template.querySelector(`.${NAMESPACE}-container`);
        const title = viewer.querySelector(`.${NAMESPACE}-title`);
        const toolbar = viewer.querySelector(`.${NAMESPACE}-toolbar`);
        const navbar = viewer.querySelector(`.${NAMESPACE}-navbar`);
        const button = viewer.querySelector(`.${NAMESPACE}-button`);
        const canvas = viewer.querySelector(`.${NAMESPACE}-canvas`);

        this.viewer = viewer;
        this.title = title;
        this.toolbar = toolbar;
        this.navbar = navbar;
        this.button = button;
        this.canvas = canvas;
        this.footer = viewer.querySelector(`.${NAMESPACE}-footer`);
        this.tooltipBox = viewer.querySelector(`.${NAMESPACE}-tooltip`);
        this.player = viewer.querySelector(`.${NAMESPACE}-player`);
        this.list = viewer.querySelector(`.${NAMESPACE}-list`);

        addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
        addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
        toggleClass(button, CLASS_HIDE, !options.button);

        if (options.backdrop) {
            addClass(viewer, `${NAMESPACE}-backdrop`);

            if (!options.inline && options.backdrop !== 'static') {
                setData(canvas, DATA_ACTION, 'hide');
            }
        }

        if (isString(options.className) && options.className) {
            // In case there are multiple class names
            options.className.split(REGEXP_SPACES).forEach((className) => {
                addClass(viewer, className);
            });
        }

        if (options.toolbar) {
            const list = document.createElement('ul');
            const custom = isPlainObject(options.toolbar);
            const zoomButtons = BUTTONS.slice(0, 3);
            const rotateButtons = BUTTONS.slice(7, 9);
            const scaleButtons = BUTTONS.slice(9);

            if (!custom) {
                addClass(toolbar, getResponsiveClass(options.toolbar));
            }

            forEach(custom ? options.toolbar : BUTTONS, (value, index) => {
                const deep = custom && isPlainObject(value);
                const name = custom ? hyphenate(index) : value;
                const show = deep && !isUndefined(value.show) ? value.show : value;

                if (
                    !show
                    || (!options.zoomable && zoomButtons.indexOf(name) !== -1)
                    || (!options.rotatable && rotateButtons.indexOf(name) !== -1)
                    || (!options.scalable && scaleButtons.indexOf(name) !== -1)
                ) {
                    return;
                }

                const size = deep && !isUndefined(value.size) ? value.size : value;
                const click = deep && !isUndefined(value.click) ? value.click : value;
                const item = document.createElement('li');

                item.setAttribute('role', 'button');
                addClass(item, `${NAMESPACE}-${name}`);

                if (!isFunction(click)) {
                    setData(item, DATA_ACTION, name);
                }

                if (isNumber(show)) {
                    addClass(item, getResponsiveClass(show));
                }

                if (['small', 'large'].indexOf(size) !== -1) {
                    addClass(item, `${NAMESPACE}-${size}`);
                } else if (name === 'play') {
                    addClass(item, `${NAMESPACE}-large`);
                }

                if (isFunction(click)) {
                    addListener(item, EVENT_CLICK, click);
                }

                list.appendChild(item);
            });

            toolbar.appendChild(list);
        } else {
            addClass(toolbar, CLASS_HIDE);
        }

        if (!options.rotatable) {
            const rotates = toolbar.querySelectorAll('li[class*="rotate"]');

            addClass(rotates, CLASS_INVISIBLE);
            forEach(rotates, (rotate) => {
                toolbar.appendChild(rotate);
            });
        }

        addClass(button, CLASS_CLOSE);
        addClass(viewer, CLASS_FIXED);
        addClass(viewer, CLASS_FADE);
        addClass(viewer, CLASS_HIDE);

        setStyle(viewer, {
            zIndex: options.zIndex,
        });

        let {container} = options;

        if (isString(container)) {
            container = element.ownerDocument.querySelector(container);
        }

        if (!container) {
            container = document.body;
        }

        container.appendChild(viewer);

        this.ready = true;

        if (isFunction(options.ready)) {
            addListener(element, EVENT_READY, options.ready, {
                once: true,
            });
        }

        if (dispatchEvent(element, EVENT_READY) === false) {
            this.ready = false;
            return;
        }
    }

    /**
     * Get the no conflict viewer class.
     * @returns {Viewer} The viewer class.
     */
    static noConflict() {
        window.Viewer = AnotherViewer;
        return Viewer;
    }

    /**
     * Change the default options.
     * @param {Object} options - The new default options.
     */
    static setDefaults(options) {
        assign(DEFAULTS, isPlainObject(options) && options);
    }
}

assign(Viewer.prototype, render, events, handlers, methods, others);

export default Viewer;
